// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace SKNotes.Configuration
{
    using System;
    using System.Reflection;

	public class PropertySetting : ValueSettingBase
	{
		public PropertySetting( object component, PropertyInfo propertyInfo ) :
			this( propertyInfo.Name, component, propertyInfo )
		{
		}

		public PropertySetting( string name, object component, PropertyInfo propertyInfo ) :
			this( name, component, propertyInfo, null )
		{
		} 

        public PropertySetting( string name, object component, PropertyInfo propertyInfo, object defaultValue ) :
			base( name, defaultValue )
		{
			if ( component == null )
			{
				throw new ArgumentNullException( "component" );
			}
			if ( propertyInfo == null )
			{
				throw new ArgumentNullException( "propertyInfo" );
			}

			this.component = component;
			this.propertyInfo = propertyInfo;
		}

		public PropertySetting( object component, string propertyName ) :
			this( propertyName, component, propertyName )
		{
		} 

		public PropertySetting( string name, object component, string propertyName ) :
			this( name, component, propertyName, null )
		{
		}

		public PropertySetting( string name, object component, string propertyName, object defaultValue ) :
			base( name, defaultValue )
		{
			if ( component == null )
			{
				throw new ArgumentNullException( "component" );
			}
			if ( string.IsNullOrEmpty( propertyName ) )
			{
				throw new ArgumentNullException( "propertyName" );
			}

			this.component = component;
			this.propertyInfo = component.GetType().GetProperty( propertyName, 
				BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
			if ( propertyInfo == null )
			{
				throw new ArgumentException( "missing setting property: " + propertyName );
			}
			if ( !propertyInfo.CanRead ) // no get; accessor
			{
				throw new ArgumentException( "setting property '" + propertyName + "' must be readable" );
			}
			if ( !propertyInfo.CanWrite ) // no set; accessor
			{
				throw new ArgumentException( "setting property '" + propertyName + "' must be writeable" );
			}
		} 

		public PropertyInfo PropertyInfo
		{
			get { return this.propertyInfo; }
		}

		public string PropertyName
		{
			get { return this.propertyInfo.Name; }
		} 

		public object Component
		{
			get { return this.component; }
		} 

		public override object OriginalValue
		{
			get { return LoadValue( Name, this.propertyInfo.PropertyType, SerializeAs, DefaultValue ); }
		}

		public override object Value
		{
			get { return this.propertyInfo.GetValue( this.component, null ); }
			set { this.propertyInfo.SetValue( this.component, value, null ); }
		} 

		public override void Load()
		{
			try
			{
				object originalValue = OriginalValue;
				if ( originalValue == null && LoadUndefinedValue == false )
				{
					return;
				}
				Value = originalValue;
			}
			catch
			{
				if ( ThrowOnErrorLoading )
				{
					throw;
				}
			}
		}

		public override void Save()
		{
			try
			{
				object value = Value;
				if ( value == null && SaveUndefinedValue == false )
				{
					return;
				}
				SaveValue( Name, this.propertyInfo.PropertyType, SerializeAs, value, DefaultValue );
			}
			catch
			{
				if ( ThrowOnErrorSaving )
				{
					throw;
				}
			}
		} 

		private readonly object component;
		private readonly PropertyInfo propertyInfo;
	}
}
